
class ContainerThing < Thing

  def add thing
    @things ||= []
    @things << thing
    thing.parent_thing = self
  end

  def go e
    children.each { |ch| ch.go(e) }
    super(e)
  end

  def can_add_more yes
    @canAddMore = yes
  end

  def recieve e
    recover_state

    if action = e.params[(form_id :action)]
      action.each {|name, data|
        next if data == ''
        if name == 'add_thing'
          add self.class.to_const(data).new
        end
      }
    end

    children.each { |child|
      child.recieve e
    }
    super
  end

  def remove thing
    @things = @things - [thing]
  end

  def move_top thing = nil, path = thing.unique_path
    (@things || []).each_index { |i|
      if path == @things[i].unique_path
        thing = @things.delete_at(i)
        @things = [thing] + @things
        break
      end
    }
  end

  def move_bottom thing = nil, path = thing.unique_path
    (@things || []).each_index { |i|
      if path == @things[i].unique_path
        @things.push(@things.delete_at(i))
        break
      end
    }
  end

  def move_up thing = nil, path = thing.unique_path
    things = children
    things.each_index { |i|
      if (i > 0) && (t = things[i]) && (path == t.unique_path)
        things[i-1], things[i] = things[i], things[i-1]
        break
      end
    }
  end

  def move_down thing = nil, path = thing.unique_path
    things = children
    len = things.length
    things.each_index { |i|
      if (i+1 < len) && (t = things[i]) && (path == t.unique_path)
        things[i+1], things[i] = things[i], things[i+1]
        break
      end
    }
  end

  def button_tree root = Thing
    items = {}
    Module.constants.each { |c|
      const = Module.const_get(c)
      if (const.is_a? Class) && (const.ancestors.include? root)
        (items[const.ancestors[1].to_s] ||= []) << c
      end
    }

    format_tree(root.to_s, items)
  end

  def format_tree root, places
    i = { :text => root, :value => root }
    if p = places[root]
      i[:submenu] ||= {:id => root+(rand()*1000).to_s }
      i[:submenu][:itemdata] = p.map { |r| format_tree r, places }
    end
    i
  end

  def header_label
    s = ''
    if @canAddNewThings != false
      s << add_button.to_s
    end
    s + super
  end

  def add_button
    action = InputHidden.new(:name => (form_id :action)+"[add_thing]")

    items = []
    thingswecanadd.each { |t|
      if tree = button_tree(t)
        items << tree
      end
    }
    items << {:text => '<hr/>'}
    items += button_tree[:submenu][:itemdata]

    options = {
      :itemdata => items,
      :action => action.id
    }

    thingswecanadd

    button = SubmitButton.new(:value => "+",
        :onclick =>
          "try { Bublifuk.add_button_click(this, #{options.to_json.gsub('"', "'")});} catch(e) { console.warn(e) } return false;"
        #:onmousedown => "Bublifuk.add_button_click(this);",
        #:onclick => " return false"
      )
    action.to_s+button.to_s
  end

  def each_thing &block
    accept_visitor &block
    children.each { |ch| ch.each_thing &block }
  end

  def visit &block
    super
    children.each {|ch| ch.visit &block }
  end

  def creator e
    s = ''
    s << children_creators(e)
    s
  end

  def children_creators e
    children.map { |child|
      child.creator_window(e).to_s
    }.join
  end

  def thingswecanadd
    [TextThing, HtmlCode, MenuThing]
  end

  def render e
    s = ''
    children.each { |thing|
      s << thing.wrap_render(e)
    }
    s
  end
  
  def children
    if !@things
      recover_state
    end
    @things ||= []
  end

  def remove_children
    @things = []
  end

  def get_data
    { 'children' => children.map(&:to_hash) }.merge(super)
  end

  def fill_data h
    if @things
      warn "multiple calls for fill_data \n  "+caller.join("\n  ")
    else
      @things = []
      (h['children'] || []).each { |child|
        add Thing.from_hash child, self
      }
      super
    end
  end



  class Test < FwTest
    def setup
      DataMapper.setup(:test, :adapter => 'in_memory')
    end

    def test_hash_includes_children_hash
      container = ContainerThing.new
      container.add child = Thing.new
      eq container.to_hash['children'], [child.to_hash]
    end

    def test_add_children_from_hash
      child = Thing.new
      container = ContainerThing.new
      container.fill_data('children' => [child.to_hash])
      eq container.children.length, 1
      eq container.children, [child]
    end
    
    def test_extract_hash
      container = ContainerThing.new
      container.add Thing.new
      h = container.to_hash

      delete = [:version, :previous_version, :changed_at, :identity]
      (h['children'] + [h]).each { |d|
        delete.each { |key|
          d.delete key.to_s
        }
      }

      assert_equal h, {
        'thing' => 'ContainerThing',
        'children' => [
          { 'thing' => 'Thing' }
        ]
      }
      #c2 = ContainerThing.from_hash(h)
    end

    def test_reconstruct_from_hash
      container = ContainerThing.new
      container.add Thing.new
      h = container.to_hash

      c2 = Thing.from_hash(h)
      assert_equal c2.class, ContainerThing
      assert_equal c2.children.length, 1
      assert_equal c2.children[0].class, Thing
    end


    def test_things_recover_their_state
      id = nil
      state_before = nil
      repository(:test) {
        t = ContainerThing.new
        text = TextThing.new
        t.add text

        text.content = 'testaaaaa'

        assert t.save
        state_before = t.state
        id = t.id
      }

      repository(:test) {
        t2 = ContainerThing.first(:id => id)
        assert_equal t2.state, state_before
        assert_equal t2.class, ContainerThing
        assert_equal t2.children.length, 1
        assert_equal t2.children[0].content, 'testaaaaa'
      }
    end

    def test_fetched_thing_can_be_saved
      id = nil
      repository(:test) {
        t = ContainerThing.new
        text = TextThing.new
        t.add text
        text.content = 'testaaaaa'
        t.save
        id = t.id
      }

      repository(:test) {
        t2 = ContainerThing.first(:id => id)
        assert t2, "thing was not persisted"
        t2.save
      }

      repository(:test) {
        t3 = ContainerThing.first(:id => id)
        assert_equal t3.children.length, 1
        assert_equal t3.children[0].content, 'testaaaaa'
      }
    end
  end

  class TestChildrenActions < FwTest
    def test_move_last_up
      container = ContainerThing.new
      container.add a = Thing.new
      container.add b = Thing.new
      container.add c = Thing.new

      a.id = "a"
      b.id = "b"
      c.id = "c"

      container.move_up c
      eq container.children.map(&:id),
        ["a", "c", "b"]
    end

    def test_move_first_down
      container = ContainerThing.new
      container.add a = Thing.new
      container.add b = Thing.new
      container.add c = Thing.new

      a.id = "a"
      b.id = "b"
      c.id = "c"

      container.move_down a
      eq container.children.map(&:id),
        ["b", "a", "c"]
    end
  end

end